package com.hky.algorithm.realize;

import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class Calculator {

    /**
     * 提供给外部调用的计算危险牌方法
     *
     * @param game 对局数据
     */
    public static void calculate(Game game) {
        Player[] players = game.getPlayers();
        Term[] terms = game.getTerms();
        clearOldDanger(terms);

        for (Player player : players) {
            if (player.alreadyHu) continue;
            doCalculate(player, terms);
        }

    }

    /**
     * 清除之前的危险牌
     *
     * @param terms term中的dangers
     */
    private static void clearOldDanger(Term[] terms) {
        for (Term term : terms) {
            Arrays.fill(term.danger, 0);
        }
    }

    /**
     * 开始计算危险牌
     *
     * @param player 没有胡牌的玩家，player.isHu == false
     * @param terms  对局数据
     */
    private static void doCalculate(Player player, Term[] terms) {
        // 最后考虑对子
        countSuiOrKe(player, terms, 0, 1, 0, Calculator::lastDuiDeal);
        // 先考虑对子
        if(player.cards_num > 1) firstSuiCalc(player, terms, Calculator::firstDuiDeal);
        // 7对
        if(player.cards_num == 13) sevenDui(player, terms, 0, 1, 0, Calculator::lastDuiDeal);
    }

    /**
     * 处理七对
     *
     * @param player     玩家
     * @param terms      所有牌组合
     * @param termsIndex 当前处理到了那种类型的牌，此参数代表的是牌的位置索引
     * @param start      处理到当前类型牌的第几张牌
     * @param floor      已经统计了多少个刻子或者对子
     * @param dangerDeal 最后的处理函数
     */
    private static void sevenDui(Player player, Term[] terms, int termsIndex, int start, int floor, LastDangerDeal dangerDeal) {
        // 已经取出所有顺子或者刻子
        if (floor == player.cards_num / 2) {
            dangerDeal.deal(player, terms);
            return;
        }

        for (int termI = termsIndex; termI < terms.length; termI++) {
            Term term = terms[termI];
            if (term.type == player.lack) continue; // 缺牌的类型跳过

            int[] cards = term.cards;
            for (int cardI = start; cardI < cards.length; cardI++) {
                int dui = term.getDui(cardI);
                if (dui != 0) sevenDui(player, terms, termI, cardI, floor + 1, dangerDeal);
                term.fillBack(dui);
            }
            start = 1;
        }
    }

    /**
     * 首先处理对子时，统计所有的顺子或刻子后，统计危险牌，这里的危险牌X能和剩下的一张牌组成顺子或刻子
     *
     * @param player 玩家
     * @param terms  对局数据
     */
    private static void firstDuiDeal(Player player, Term[] terms) {
        for (Term term : terms) {
            if (term.type == player.lack) continue; // 跳过玩家的缺牌
            term.addToDangerHuSunOrKe();
        }
    }

    /**
     * 统计所有的顺子或刻子后，统计危险牌，这里的危险牌X能和剩下的一张牌组成对子
     *
     * @param player 玩家
     * @param terms  对局数据
     */
    private static void lastDuiDeal(Player player, Term[] terms) {
        for (Term term : terms) {
            if (term.type == player.lack) continue; // 跳过玩家的缺牌
            term.addToDangerHuDui();
        }
    }

    /**
     * 优先统计对子，然后统计剩下的刻子或者对子，最后还剩下两张牌，如果最后剩下的两张牌能和另外一张牌X组合成顺子或者刻子的话就将X添加到危险牌
     *
     * @param player     玩家
     * @param terms      所有牌组合
     * @param dangerDeal 最后的处理函数
     */
    private static void firstSuiCalc(Player player, Term[] terms, LastDangerDeal dangerDeal) {
        player.cards_num -= 2;
        for (Term term : terms) {
            if (term.type == player.lack) continue; // 缺牌的类型跳过

            int[] cards = term.cards;
            for (int cardI = 1; cardI < cards.length; cardI++) {
                int dui = term.getDui(cardI);
                if (dui != 0) countSuiOrKe(player, terms, 0, 1, 0, dangerDeal);
                term.fillBack(dui);
            }
        }
        player.cards_num += 2;
    }

    /**
     * 统计出所有的顺子或者刻子后，最后根据LastDangerDeal处理要胡的牌
     *
     * @param player     玩家
     * @param terms      所有牌组合
     * @param termsIndex 当前处理到了那种类型的牌，此参数代表的是牌的位置索引
     * @param start      处理到当前类型牌的第几张牌
     * @param floor      已经统计了多少个刻子或者对子
     * @param dangerDeal 最后的处理函数
     */
    private static void countSuiOrKe(Player player, Term[] terms, int termsIndex, int start, int floor, LastDangerDeal dangerDeal) {
        // 已经取出所有顺子或者刻子
        if (floor == player.cards_num / 3) {
            dangerDeal.deal(player, terms);
            return;
        }

        // 取出一个刻子或者顺子
        for (int termI = termsIndex; termI < terms.length; termI++) {
            Term term = terms[termI];
            if (term.type == player.lack) continue; // 缺牌的类型跳过

            int[] cards = term.cards;
            for (int cardI = start; cardI < cards.length; cardI++) {
                int[] sunOrKe = term.getKe(cardI);
                if (sunOrKe != null) countSuiOrKe(player, terms, termI, cardI, floor + 1, dangerDeal);
                term.fillBack(sunOrKe);

                sunOrKe = term.getSun(cardI);
                if (sunOrKe != null) countSuiOrKe(player, terms, termI, cardI, floor + 1, dangerDeal);
                term.fillBack(sunOrKe);
            }
            start = 1; // 当前类型的牌已经计算结束，开始计算另外一种类型的牌，初始化位置为1
        }
    }
}
